/* Copyright © 2010 Brandon L Black
 *
 * This file is part of vscf.
 *
 * vscf is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * vscf is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with vscf.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Brandon L Black <blblack@gmail.com>
 */

#ifndef _VSCF_H
#define _VSCF_H

#include <stdio.h>
#include <stdbool.h>
#include <sys/types.h>

// _RC = const for user code, not const for library code
#ifdef _IN_VSCF_C
#define _RC
#else
#define _RC const
#endif

// Opaque data type used for all complex data pointers in the public API
typedef union _vscf_data_t vscf_data_t;

// Used in hash sorting callbacks
typedef struct {
    const unsigned     len;
    const char* const  key;
} vscf_key_t;

// Callback function pointer typedefs for error messages and memory management
typedef void  (*vscf_err_cb_t)(const char*, ...);
typedef void* (*vscf_calloc_cb_t)(const size_t, const size_t);
typedef void* (*vscf_realloc_cb_t)(void*, const size_t);
typedef void  (*vscf_free_cb_t)(void*);

// Functions to set the error and memory management callbacks
void vscf_set_err_cb(vscf_err_cb_t e);
void vscf_set_calloc_cb(vscf_calloc_cb_t c);
void vscf_set_realloc_cb(vscf_realloc_cb_t r);
void vscf_set_free_cb(vscf_free_cb_t f);

// Invokes the scanner, returning the root-level hash on success
const vscf_data_t* vscf_scan_filename(const char* fn);
const vscf_data_t* vscf_scan_fd(int fd, const char* desc);
const vscf_data_t* vscf_scan_stream(FILE* stream, const char* desc);
const vscf_data_t* vscf_scan_string(const char* s, unsigned len, const char* desc);

// Destroys (de-allocates) the entire tree of data returned by vscf_scan()
//  Do not call on sub-elements, only on the value actually returned by vscf_scan().
void vscf_destroy(_RC vscf_data_t* d);

/*
 * These are the data types vscf_get_type (below) can return.
 * vscf_simple_ functions can only be called on data of type VSCF_SIMPLE_T
 * vscf_array_ functions can only be called on data of type VSCF_ARRAY_T
 * vscf_hash_ functions can only be called on data of type VSCF_HASH_T
 */
typedef enum {
    VSCF_HASH_T,
    VSCF_ARRAY_T,
    VSCF_SIMPLE_T
} vscf_type_t;

// Get the type of an otherwise opaque "const vscf_data_t*"
vscf_type_t vscf_get_type(const vscf_data_t* d);

/* Returns the containing parent array or hash, or NULL if
   called on the root of the configuration */
const vscf_data_t* vscf_get_parent(const vscf_data_t* d);

/*** Various type-specific accessor functions: ***/

// Get the length of a simple string value
unsigned vscf_simple_get_len(const vscf_data_t* d);

// Get a const pointer to the simple string itself (note that
//  the format allows embedded NULs, hence the need for a length).
// Also note that all simple strings get an extra NUL terminator
//  one byte past the official end of string data.  For many cases
//  this allows one to use them as NUL-terminated strings.
const char* vscf_simple_get_data(const vscf_data_t* d);

// Return value indicates type-conversion success or failure (whether the
//  data was exactly convertible), output stored in out.  The numeric
//  conversions are per the rules of strtoul, strtol, and strtod and must
//  consume the entire string.  The bool conversion requires the data
//  to be the string "true" or "false" in any mix of upper/lower case.
bool vscf_simple_get_as_ulong(const vscf_data_t* d, unsigned long* out);
bool vscf_simple_get_as_long(const vscf_data_t* d, long* out);
bool vscf_simple_get_as_double(const vscf_data_t* d, double* out);
bool vscf_simple_get_as_bool(const vscf_data_t* d, bool* out);

// Get the length of an array.  Zero means the array is empty.
unsigned vscf_array_get_len(const vscf_data_t* d);

// Get a member of an array.  idx must be less than the length returned above. 
const vscf_data_t* vscf_array_get_item(const vscf_data_t* d, unsigned idx);

// Get the count of keys in the hash
unsigned vscf_hash_get_nkeys(const vscf_data_t* d);

// Get a member of a hash by key.
// The _constk version is a convenience macro for the common
//  case where the key is a constant string, as it fills in the length
//  for you via sizeof.
// set_mark will mark any entries successfully retrieved, which affects, _iterate below.
const vscf_data_t* vscf_hash_get_item(const vscf_data_t* d, const char* key, unsigned klen, bool set_mark);
#define vscf_hash_get_item_constk(_d, _key, _sm) \
    vscf_hash_get_item((_d), (_key), (sizeof(_key) - 1), (_sm))

/*
 * Iterate all members of a hash with a used-supplied callback function.
 * Return from your callback with true normally, or false to prematurely terminate iteration.
 * The "data" argument is passed verbatim as the final argument to your callback.  You can
 *  use (or not use) it however you wish.
 * Hash keys are iterated in the order they appeared in the input file.
 * If "ignore_mark" is true, vscf_hash_iterate will skip (not issue callbacks for) any hash
 *  elements which were marked via the "set_mark" option of vscf_hash_get_item earlier.
 * This is a generic mechanism for "retrieve certain keys explicitly one by one, and then iterate
 *  the remainder of the keys", which is useful in a lot of scenarios.  e.g., if you
 *  only have a limited set of explicitly legal keys in a given part of your configfile format,
 *  you can _get_item them all, and then run _iterate with a callback that generates an "illegal key"
 *  error.
 */
typedef bool (*vscf_hash_iter_cb_t)(const char* key, unsigned klen, const vscf_data_t* d, void* data);
void vscf_hash_iterate(const vscf_data_t* d, bool ignore_mark, vscf_hash_iter_cb_t f, void* data);

// Re-sort hash keys from default order (order defined in config file) to an arbitary
//  order of your choosing, using a qsort()-like compare callback.  Calls to vscf_hash_iterate
//  after vscf_hash_sort will iterate in the new sort order.  Not thread-safe (all access to
//  a given hash should be locked if it's being sorted in a threaded environment).
typedef int (*vscf_key_cmp_cb_t)(const vscf_key_t* const * const a, const vscf_key_t* const * const b);
void vscf_hash_sort(const vscf_data_t* d, vscf_key_cmp_cb_t f);

/*
 * Dump a data value (simple, array, or hash) to a FILE* stream.
 * Because the data is normalized with escape sequences, there
 *  will be no embedded NULs.  All keys and string values will
 *  be quoted, with all characters outside the 0x20-0x7E range
 *  escaped using characters within said range.  Array and Hash
 *  members will be separated by ",\n", and the output will be
 *  indented to show structure.
 * It is intended that once an input has been parsed and dumped
 *  to a file, further parsing and dumping would produce
 *  identical output.  That is to say: given...
 *    x = vscf_scan_*(...);
 *    vscf_dump(x, some_file1);
 *    x1 = vscf_scan_*(some_file1);
 *    vscf_dump(x1, some_file2);
 *    x2 = vscf_scan_*(some_file2);
 *    vscf_dump(x2, some_file3);
 *  ... the x, x1, and x2 data structures would have identical content,
 *  and the some_file[123] files would be byte-identical (assuming they
 *  were freshly opened output streams to files).
 */
void vscf_dump(const vscf_data_t* d, FILE* stream);

#undef _RC

#endif /* _VSCF_H */
